﻿using HMaker.Events;
using HMaker.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace HMaker.Controls
{
    /// <summary>
    /// 图形控件基类
    /// </summary>
    public abstract class ShapeControl : Control
    {
        private enum CursorPos
        {
            N, S, W, E,
            NW, NE, SW, SE,
            None
        }

        #region 对外接口

        /// <summary>
        /// 当本图形被选中时引发
        /// </summary>
        public event EventHandler Selected;

        /// <summary>
        /// 当此控件触发上下文菜单时
        /// </summary>
        public event ContextMenuNeededEventHandler OnContextMenuNeeded;

        /// <summary>
        /// 当图形的绝对位置被通过移动改变后,触发此事件
        /// </summary>
        public event EventHandler AbsoluteCenterPosChanged;

        /// <summary>
        /// 与此图形控件绑定的坐标原点
        /// </summary>
        public OriginPos OriginPos { get; }

        /// <summary>
        /// 读取或设置图形的中心点(绝对坐标(相对于容器))
        /// </summary>
        public virtual Point CenterPos
        {
            get => new Point(Left + Width / 2, Top + Height / 2);
            set => Location = new Point(value.X - Width / 2, value.Y - Height / 2);
        }

        /// <summary>
        /// 更新相对于原点的坐标
        /// </summary>
        protected void UpdateRelativeCenterPos()
        {
            if (IsBound && OriginPos != null)
            {
                _RelativeCenterPos = OriginPos.CalcOffset(CenterPos);
            }
        }

        /// <summary>
        /// 原点到图形的中心的偏移,
        /// </summary>
        private Point _RelativeCenterPos;

        /// <summary>
        /// 表明此图形控件是否已经绑定到坐标原点
        /// </summary>
        private bool _IsBound = false;

        /// <summary>
        /// 读取或设置图形的中心点(相对坐标(相对于OriginPos))
        /// </summary>
        public Point RelativeCenterPos
        {
            get => _RelativeCenterPos;
            set
            {
                _RelativeCenterPos = value;
                CenterPos = OriginPos?.CalcLocation(value) ?? throw new NullReferenceException("没有设置基准点,无法设置相对坐标");
            }
        }

        /// <summary>
        /// 此图形组件是否已经绑定到坐标原点
        /// </summary>
        public bool IsBound
        {
            get => _IsBound;
            set
            {
                if (value)
                {
                    _RelativeCenterPos = OriginPos?.CalcOffset(CenterPos) ?? throw new NullReferenceException("没有设置基准点,无法进行绑定");
                }

                _IsBound = value;
            }
        }

        /// <summary>
        /// 是否允许调整图形大小
        /// </summary>
        public virtual bool CanReszie { get; set; } = true;

        /// <summary>
        /// 是否允许图形移动位置
        /// </summary>
        public virtual bool CanMove { get; set; } = true;

        /// <summary>
        /// 克隆当前图形
        /// </summary>
        /// <returns></returns>
        public abstract ShapeControl Clone();

        /// <summary>
        /// 获取图片的配置文件描述,用于将各种形状序列化为参数,以便存储,
        /// 子类实现了此接口后,还需要实现GetShapeInstance接口,以便可以通过字符反序列化为对应的对象
        /// </summary>
        public virtual KeyValuePair<string, string> ShapeDescription => throw new NotImplementedException();


        #endregion
        /// <summary>
        /// 使用ShapeDescription生成的数据反序列化出一个对象
        /// </summary>
        /// <param name="type"></param>
        /// <param name="param"></param>
        /// <param name="origin"></param>
        /// <returns></returns>
        public static ShapeControl GetShapeInstance(string type, string param, OriginPos origin = null)
        {
            switch (type?.ToUpper())
            {
                case "SHAPERECTANGLE":
                    {
                        if (Util.SplitParamAsInt(param, 4, out int[] result, ','))
                        {
                            return new ShapeRectangle(new Point(result[0], result[1]), new Size(result[2], result[3]), origin);
                        }
                    }
                    break;
                //case "SHAPEPARALLELOGRAM":
                //    {
                //        if (Utils.SplitParamAsInt(param, 5, out int[] result, ','))
                //        {
                //            return new ShapeParallelogram(new Point(result[0], result[1]), result[2], result[3], result[4], origin);
                //        }
                //    }
                //    break;
                //case "SHAPEELLIPSE":
                //    {
                //        if (Utils.SplitParamAsInt(param, 4, out int[] result, ','))
                //        {
                //            return new ShapeEllipse(new Point(result[0], result[1]), new Size(result[2], result[3]), origin);
                //        }
                //    }
                //    break;
                case null:
                    throw new ArgumentNullException("type");
                default:
                    throw new NotImplementedException("未实现的类型,无法进行转换");
            }
            return null;
        }


        public ShapeControl(OriginPos origin = null)
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.ResizeRedraw, true);
            BackColor = Color.Transparent;
            DoubleBuffered = true;
            OriginPos = origin;
            if (origin != null)
            {
                origin.LocationChanged += Origin_LocationChanged;
            }
        }

        private void Origin_LocationChanged(object sender, EventArgs e)
        {
            if (IsBound && OriginPos != null)
            {
                CenterPos = OriginPos.CalcLocation(_RelativeCenterPos);
            }
        }

        #region 预处理图形的数据
        /// <summary>
        /// 缓存画刷
        /// </summary>
        protected Brush ForeBrush = Brushes.Brown;

        /// <summary>
        /// 框架的大小
        /// </summary>
        protected Size FrameSize = Size.Empty;

        /// <summary>
        /// 当前景色发生改变时,更新画刷资源
        /// </summary>
        /// <param name="e"></param>
        protected override void OnForeColorChanged(EventArgs e)
        {
            base.OnForeColorChanged(e);
            ForeBrush = new SolidBrush(ForeColor);
        }

        /// <summary>
        /// 用于更新内部参数,子类如果有需要更新数据,则需要继承此函数
        /// </summary>
        protected virtual void UpdateParam()
        {
        }

        /// <summary>
        /// 大小改变时,修改缓存数据
        /// </summary>
        /// <param name="e"></param>
        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            FrameSize = new Size(Size.Width - 1, Size.Height - 1);
            UpdateParam();
            if (!IsDrag)
            {
                UpdateRegion();
                AbsoluteCenterPosChanged?.Invoke(this, EventArgs.Empty);
            }
            UpdateRelativeCenterPos();
        }

        /// <summary>
        /// 当控件被拖动时执行
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLocationChanged(EventArgs e)
        {
            base.OnLocationChanged(e);
            UpdateRelativeCenterPos();
            if (!IsDrag)
            {
                AbsoluteCenterPosChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// 自动更新图形的区域
        /// </summary>
        private void UpdateRegion()
        {
            if (IsSelect)
            {
                Region = null;
            }
            else
            {
                GraphicsPath path = OnDrawRegion();
                Region = path != null ? new Region(path) : null;
            }
        }

        /// <summary>
        /// 每一个形状都应当手动实现此函数,用于自动裁剪图形的形状
        /// </summary>
        /// <returns></returns>
        protected abstract GraphicsPath OnDrawRegion();

        #endregion

        #region 控件的绘制与行为
        private bool _IsSelect = false;
        /// <summary>
        /// 表示当前图形是否正激活
        /// </summary>
        public bool IsSelect
        {
            get => _IsSelect;
            set
            {
                _IsSelect = value;
                BringToFront();
                Invalidate();
            }
        }

        /// <summary>
        ///表示当前的图形是否正在被拖拽
        /// </summary>
        protected bool IsDrag = false;

        /// <summary>
        /// 记录是否发生过移动,以触发事件
        /// </summary>
        private bool HadMove = false;

        /// <summary>
        /// 图形被点击时的偏移(用于计算新的位置)
        /// </summary>
        private Point _LastOffset = Point.Empty;

        /// <summary>
        /// 选择时使用的画笔
        /// </summary>
        private readonly Pen SelectPen = new Pen(Color.LightGray) { DashStyle = DashStyle.Dot };

        /// <summary>
        /// 指定大小调整光标出现的区域的大小
        /// </summary>
        private const int SelectRange = 3;

        /// <summary>
        /// 用于记录鼠标左键按下时的模式,用于调整图形大小
        /// </summary>
        private CursorPos CurCursorMode = CursorPos.None;

        /// <summary>
        /// 绘图鼠标经过时的虚框
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (IsSelect)
            {
                e.Graphics.DrawRectangle(SelectPen, new Rectangle(Point.Empty, FrameSize));
            }
        }

        /// <summary>
        /// 鼠标在形状上时作出响应
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            IsSelect = true;
            BringToFront();
            Region = null;
            //Invalidate();
        }

        /// <summary>
        /// 鼠标离开图形时做出响应
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            IsSelect = false;
            GraphicsPath path = OnDrawRegion();
            Region = path != null ? new Region(path) : null;
            //Invalidate();
        }

        /// <summary>
        /// 判断当前所需的鼠标光标图案
        /// </summary>
        /// <param name="location"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        private Cursor JudgeCursor(Point location, out CursorPos pos)
        {
            if (CanReszie)
            {
                if (location.X <= SelectRange)
                {
                    if (location.Y <= SelectRange)
                    {
                        pos = CursorPos.NW;
                        return Cursors.SizeNWSE;
                    }
                    else if (FrameSize.Height - location.Y <= SelectRange)
                    {
                        pos = CursorPos.SW;
                        return Cursors.SizeNESW;
                    }
                    else
                    {
                        pos = CursorPos.W;
                        return Cursors.SizeWE;
                    }
                }
                else if (FrameSize.Width - location.X <= SelectRange)
                {
                    if (location.Y <= SelectRange)
                    {
                        pos = CursorPos.NE;
                        return Cursors.SizeNESW;
                    }
                    else if (FrameSize.Height - location.Y <= SelectRange)
                    {
                        pos = CursorPos.SE;
                        return Cursors.SizeNWSE;
                    }
                    else
                    {
                        pos = CursorPos.E;
                        return Cursors.SizeWE;
                    }
                }
                else
                {
                    if (location.Y <= SelectRange)
                    {
                        pos = CursorPos.N;
                        return Cursors.SizeNS;
                    }
                    else if (FrameSize.Height - location.Y <= SelectRange)
                    {
                        pos = CursorPos.S;
                        return Cursors.SizeNS;
                    }
                }
            }
            pos = CursorPos.None;
            return CanMove ? Cursors.SizeAll : Cursors.Default;
        }

        /// <summary>
        /// 计算鼠标偏移量
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        private Size CalcMove(MouseEventArgs e)
        {
            return new Size(e.X - _LastOffset.X, e.Y - _LastOffset.Y);
        }

        /// <summary>
        /// 鼠标开始按下时
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button == MouseButtons.Left)
            {
                _LastOffset = e.Location;
                if (CanMove)
                {
                    IsDrag = true;
                    Cursor = JudgeCursor(e.Location, out CurCursorMode);
                    HadMove = false;
                }
                Selected?.Invoke(this, EventArgs.Empty);
            }
            else if (e.Button == MouseButtons.Right)
            {
                OnContextMenuNeeded?.Invoke(this, new ContextMenuNeededEventArgs()
                {
                    Location = (Parent?.PointToClient(PointToScreen(e.Location))) ?? Point.Empty,
                    Rect = Bounds,
                    IsInArea = false,
                    Control = this
                });
            }
        }

        /// <summary>
        /// 鼠标进行移动时的处理
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (IsDrag)
            {
                switch (CurCursorMode)
                {
                    case CursorPos.None:
                        Location = Point.Add(Location, CalcMove(e));
                        break;
                    case CursorPos.N:
                        {
                            Size size = new Size(0, CalcMove(e).Height);
                            Location = Point.Add(Location, size);
                            Height -= size.Height;
                        }
                        break;
                    case CursorPos.S:
                        {
                            int dh = CalcMove(e).Height;
                            Height += dh;
                            _LastOffset.Offset(0, dh);
                        }
                        break;
                    case CursorPos.W:
                        {
                            Size size = new Size(CalcMove(e).Width, 0);
                            Location = Point.Add(Location, size);
                            Width -= size.Width;
                        }
                        break;
                    case CursorPos.E:
                        {
                            int w = CalcMove(e).Width;
                            Width += w;
                            _LastOffset.Offset(w, 0);
                        }
                        break;
                    case CursorPos.NW:
                        {
                            Size size = CalcMove(e);
                            Location = Point.Add(Location, size);
                            Size = Size.Subtract(Size, size);
                        }
                        break;
                    case CursorPos.NE:
                        {
                            Size size = CalcMove(e);
                            _LastOffset.Offset(size.Width, 0);
                            Top += size.Height;
                            Width += size.Width;
                            Height -= size.Height;
                        }
                        break;
                    case CursorPos.SW:
                        {
                            Size size = CalcMove(e);
                            _LastOffset.Offset(0, size.Height);
                            Left += size.Width;
                            Width -= size.Width;
                            Height += size.Height;
                        }
                        break;
                    case CursorPos.SE:
                        {
                            Size size = CalcMove(e);
                            _LastOffset.Offset(size.Width, size.Height);
                            Size = Size.Add(Size, size);
                        }
                        break;
                }
                HadMove = true;
            }
            else
            {
                Cursor = JudgeCursor(e.Location, out _);
            }
        }


        /// <summary>
        /// 鼠标按键弹起时的处理
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == MouseButtons.Left)
            {
                if (CanMove)
                {
                    IsDrag = false;
                    if (HadMove)
                    {
                        AbsoluteCenterPosChanged?.Invoke(this, EventArgs.Empty);
                    }
                }
            }

        }
        #endregion
    }

}
